package biosystems;

import edu.vanderbilt.isis.sm.Event;
import edu.vanderbilt.isis.sm.StateflowInterpreter;
import edu.vanderbilt.isis.sm.SymbolicDataProvider;

public class BioSystem {
	// Anchor cell
	TopLevelAnchorCell ac;

	// Six Vulval Precursor Cells
	TopLevelVPC p3p;
	TopLevelVPC p4p;
	TopLevelVPC p5p;
	TopLevelVPC p6p;
	TopLevelVPC p7p;
	TopLevelVPC p8p;

	// Timer
	TopLevelTimer t;
	
	// Clock
	TopLevelClock c;

	public BioSystem() {
		// New instances of AC and VPCs
		this.ac = new TopLevelAnchorCell();

		this.p3p = new TopLevelVPC();
		this.p4p = new TopLevelVPC();
		this.p5p = new TopLevelVPC();
		this.p6p = new TopLevelVPC();
		this.p7p = new TopLevelVPC();
		this.p8p = new TopLevelVPC();

		this.t = new TopLevelTimer();

		this.c = new TopLevelClock();
		
		// Readers
		// not really used
		AnchorCellReader acReader = new AnchorCellReader(this.ac.r1.AnchorCell3, new SymbolicDataProvider(1), null);
		
		VPCReader p3pReader = new VPCReader(this.p3p.r1.VPC3, new SymbolicDataProvider(1), null);
		VPCReader p4pReader = new VPCReader(this.p4p.r1.VPC3, new SymbolicDataProvider(1), null);
		VPCReader p5pReader = new VPCReader(this.p5p.r1.VPC3, new SymbolicDataProvider(1), null);
		VPCReader p6pReader = new VPCReader(this.p6p.r1.VPC3, new SymbolicDataProvider(1), null);
		VPCReader p7pReader = new VPCReader(this.p7p.r1.VPC3, new SymbolicDataProvider(1), null);
		VPCReader p8pReader = new VPCReader(this.p8p.r1.VPC3, new SymbolicDataProvider(1), null);
		
		TimerReader tReader = new TimerReader(this.t.r1.Timer3, new SymbolicDataProvider(1),null);

		ClockReader cReader = new ClockReader(this.c.r1.Clock3, new SymbolicDataProvider(1), null);
		
		// Stateflow interpreters
		this.ac.sim = new StateflowInterpreter(this.ac.sm, acReader);

		this.p3p.sim = new StateflowInterpreter(this.p3p.sm, p3pReader);
		this.p4p.sim = new StateflowInterpreter(this.p4p.sm, p4pReader);
		this.p5p.sim = new StateflowInterpreter(this.p5p.sm, p5pReader);
		this.p6p.sim = new StateflowInterpreter(this.p6p.sm, p6pReader);
		this.p7p.sim = new StateflowInterpreter(this.p7p.sm, p7pReader);
		this.p8p.sim = new StateflowInterpreter(this.p8p.sm, p8pReader);

		this.t.sim = new StateflowInterpreter(this.t.sm, tReader);

		this.c.sim = new StateflowInterpreter(this.c.sm, cReader);
		
		// Initialize all
		this.ac.sim.initialize();
		
		this.p3p.sim.initialize();
		this.p4p.sim.initialize();
		this.p5p.sim.initialize();
		this.p6p.sim.initialize();
		this.p7p.sim.initialize();
		this.p8p.sim.initialize();
		
		this.t.sim.initialize();
		this.c.sim.initialize();
	}

	public void initVPC(TopLevelVPC vpc, int mut_lst, int mut_let23, int mut_sem5, int mut_let60, int mut_mpk1, int mut_lin12, int mut_dep1) {
		// lst
		if (mut_lst == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.lst = vpc.r1.VPC3.off;
		else // mut_lst == wt
			vpc.r1.VPC3.lst = vpc.r1.VPC3.low;

		// LS
		vpc.r1.VPC3.LS = vpc.r1.VPC3.low;

		// sur2
		vpc.r1.VPC3.sur2 = vpc.r1.VPC3.low;

		// let23
		if (mut_let23 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.let23 = vpc.r1.VPC3.off;
		else // mut_lst == wt
			vpc.r1.VPC3.let23 = vpc.r1.VPC3.low;

		// sem5
		if (mut_sem5 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.sem5 = vpc.r1.VPC3.off;
		else if (mut_sem5 == vpc.r1.VPC3.rf)
			vpc.r1.VPC3.sem5 = vpc.r1.VPC3.low;
		else // mut_sem5 == wt
			vpc.r1.VPC3.sem5 = vpc.r1.VPC3.low;

		// let60
		if (mut_let60 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.let60 = vpc.r1.VPC3.off;
		else if (mut_let60 == vpc.r1.VPC3.wt)
			vpc.r1.VPC3.let60 = vpc.r1.VPC3.low;
		else // mut_let60 == gf
			vpc.r1.VPC3.let60 = vpc.r1.VPC3.med;

		// mpk1
		if (mut_mpk1 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.mpk1 = vpc.r1.VPC3.off;
		else // mut_dep1 == wt
			vpc.r1.VPC3.mpk1 = vpc.r1.VPC3.low;


		// lin12
		if (mut_lin12 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.lin12 = vpc.r1.VPC3.off;
		else if (mut_lin12 == vpc.r1.VPC3.wt)
			vpc.r1.VPC3.lin12 = vpc.r1.VPC3.low;
		else // mut_lin12 == gf
			vpc.r1.VPC3.lin12 = vpc.r1.VPC3.high;

		// dep1
		if (mut_dep1 == vpc.r1.VPC3.ko)
			vpc.r1.VPC3.dep1 = vpc.r1.VPC3.off;
		else // mut_dep1 == wt
			vpc.r1.VPC3.dep1 = vpc.r1.VPC3.low;

		// fate
		vpc.r1.VPC3.fate = vpc.r1.VPC3.af;
		// cellcycle
		vpc.r1.VPC3.cellcycle = vpc.r1.VPC3.G1phase;
		// counter
		vpc.r1.VPC3.counter = 0;
	}

	
	/**
	 * not used.
	 */
	public void initAC(int mut_ac, int mut_lin3, int mut_lin15){

		// lin3 & ISdN
		if (mut_ac == this.ac.r1.AnchorCell3.formed) {
			// lin3
			this.ac.r1.AnchorCell3.lin3 = this.ac.r1.AnchorCell3.med;
			// ISdN
			this.ac.r1.AnchorCell3.ISd0 = this.ac.r1.AnchorCell3.high;
			this.ac.r1.AnchorCell3.ISd1 = this.ac.r1.AnchorCell3.med;
			this.ac.r1.AnchorCell3.ISd2 = this.ac.r1.AnchorCell3.low1;
			this.ac.r1.AnchorCell3.ISd3 = this.ac.r1.AnchorCell3.low;

		} else {// mut_ac == ablated
			// lin3
			this.ac.r1.AnchorCell3.lin3 = this.ac.r1.AnchorCell3.off;
			// ISdN
			this.ac.r1.AnchorCell3.ISd0 = this.ac.r1.AnchorCell3.low;
			this.ac.r1.AnchorCell3.ISd1 = this.ac.r1.AnchorCell3.low;
			this.ac.r1.AnchorCell3.ISd2 = this.ac.r1.AnchorCell3.low;
			this.ac.r1.AnchorCell3.ISd3 = this.ac.r1.AnchorCell3.low;
		}

		if (mut_lin15 == this.ac.r1.AnchorCell3.wt)
			this.ac.r1.AnchorCell3.lin15 = this.ac.r1.AnchorCell3.on;
		else if (mut_lin15 == this.ac.r1.AnchorCell3.ko)
			this.ac.r1.AnchorCell3.lin15 = this.ac.r1.AnchorCell3.off;
		else // mut_lin15 == rf
			this.ac.r1.AnchorCell3.lin15 = this.ac.r1.AnchorCell3.off;

	}

	/**
	 * Set initial bindings between AnchorCell and VPCs.
	 * Should be executed symbolically
	 */
	public void initAll(int mut_ac, int mut_lin3, int mut_lin15, int mut_lin12, int mut_let23, int mut_sem5, int mut_let60, int mut_mpk1, int mut_dep1, int mut_lst) {
		this.ac.r1.AnchorCell3.a_ac = mut_ac;
		this.ac.r1.AnchorCell3.a_lin3 = mut_lin3;
		this.ac.r1.AnchorCell3.a_lin15 = mut_lin15;
		
		this.p3p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd3;
		this.p3p.r1.VPC3.LSleft = this.ac.r1.AnchorCell3.low;
		this.p3p.r1.VPC3.LSright = this.p4p.r1.VPC3.LS;
		this.p3p.r1.VPC3.v_lin12 = mut_lin12;
		this.p3p.r1.VPC3.v_let23 = mut_let23;
		this.p3p.r1.VPC3.v_sem5 = mut_sem5;
		this.p3p.r1.VPC3.v_let60 = mut_let60;
		this.p3p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p3p.r1.VPC3.v_dep1 = mut_dep1;
		this.p3p.r1.VPC3.v_lst = mut_lst;
		this.p3p.r1.VPC3.go = this.t.r1.Timer3.var1;
		this.p3p.r1.VPC3.time = this.c.r1.Clock3.time;
		
		this.p4p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd2;
		this.p4p.r1.VPC3.LSleft = this.p3p.r1.VPC3.LS;
		this.p4p.r1.VPC3.LSright = this.p5p.r1.VPC3.LS;
		this.p4p.r1.VPC3.v_lin12 = mut_lin12;
		this.p4p.r1.VPC3.v_let23 = mut_let23;
		this.p4p.r1.VPC3.v_sem5 = mut_sem5;
		this.p4p.r1.VPC3.v_let60 = mut_let60;
		this.p4p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p4p.r1.VPC3.v_dep1 = mut_dep1;
		this.p4p.r1.VPC3.v_lst = mut_lst;
		this.p4p.r1.VPC3.go = this.t.r1.Timer3.var2;
		this.p4p.r1.VPC3.time = this.c.r1.Clock3.time;
	
		this.p5p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd1;
		this.p5p.r1.VPC3.LSleft = this.p4p.r1.VPC3.LS;
		this.p5p.r1.VPC3.LSright = this.p6p.r1.VPC3.LS;
		this.p5p.r1.VPC3.v_lin12 = mut_lin12;
		this.p5p.r1.VPC3.v_let23 = mut_let23;
		this.p5p.r1.VPC3.v_sem5 = mut_sem5;
		this.p5p.r1.VPC3.v_let60 = mut_let60;
		this.p5p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p5p.r1.VPC3.v_dep1 = mut_dep1;
		this.p5p.r1.VPC3.v_lst = mut_lst;
		this.p5p.r1.VPC3.go = this.t.r1.Timer3.var3;
		this.p5p.r1.VPC3.time = this.c.r1.Clock3.time;
		
		this.p6p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd0;
		this.p6p.r1.VPC3.LSleft = this.p5p.r1.VPC3.LS;
		this.p6p.r1.VPC3.LSright = this.p7p.r1.VPC3.LS;
		this.p6p.r1.VPC3.v_lin12 = mut_lin12;
		this.p6p.r1.VPC3.v_let23 = mut_let23;
		this.p6p.r1.VPC3.v_sem5 = mut_sem5;
		this.p6p.r1.VPC3.v_let60 = mut_let60;
		this.p6p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p6p.r1.VPC3.v_dep1 = mut_dep1;
		this.p6p.r1.VPC3.v_lst = mut_lst;
		this.p6p.r1.VPC3.go = this.t.r1.Timer3.var4;
		this.p6p.r1.VPC3.time = this.c.r1.Clock3.time;
		
		this.p7p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd1;
		this.p7p.r1.VPC3.LSleft = this.p6p.r1.VPC3.LS;
		this.p7p.r1.VPC3.LSright = this.p8p.r1.VPC3.LS;
		this.p7p.r1.VPC3.v_lin12 = mut_lin12;
		this.p7p.r1.VPC3.v_let23 = mut_let23;
		this.p7p.r1.VPC3.v_sem5 = mut_sem5;
		this.p7p.r1.VPC3.v_let60 = mut_let60;
		this.p7p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p7p.r1.VPC3.v_dep1 = mut_dep1;
		this.p7p.r1.VPC3.v_lst = mut_lst;
		this.p7p.r1.VPC3.go = this.t.r1.Timer3.var5;
		this.p7p.r1.VPC3.time = this.c.r1.Clock3.time;
		
		this.p8p.r1.VPC3.IS = this.ac.r1.AnchorCell3.ISd2;
		this.p8p.r1.VPC3.LSleft = this.p7p.r1.VPC3.LS;
		this.p8p.r1.VPC3.LSright = this.p8p.r1.VPC3.low;
		this.p8p.r1.VPC3.v_lin12 = mut_lin12;
		this.p8p.r1.VPC3.v_let23 = mut_let23;
		this.p8p.r1.VPC3.v_sem5 = mut_sem5;
		this.p8p.r1.VPC3.v_let60 = mut_let60;
		this.p8p.r1.VPC3.v_mpk1 = mut_mpk1;
		this.p8p.r1.VPC3.v_dep1 = mut_dep1;
		this.p8p.r1.VPC3.v_lst = mut_lst;
		this.p8p.r1.VPC3.go = this.t.r1.Timer3.var6;
		this.p8p.r1.VPC3.time = this.c.r1.Clock3.time;
	}

	public void runMachines() {
		// Set initial values. This will be a symbolic method.
	
		printAll("AC and VPCs before executing any transition:");
		
		initAll(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
		//		initAC(mut_ac, mut_lin3, mut_lin15);
		//		initVPC(this.p3p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);
		//		initVPC(this.p4p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);
		//		initVPC(this.p5p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);
		//		initVPC(this.p6p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);
		//		initVPC(this.p7p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);
		//		initVPC(this.p8p, mut_lst, mut_let23, mut_sem5, mut_let60, mut_mpk1, mut_lin12, mut_dep1);

		// Create empty event
		Event e = new Event("");

		// Pass the empty event to each of the components
		// and execute a first step
		this.ac.sim.addEvent(e);  this.ac.sim.step();

		this.p3p.sim.addEvent(e); this.p3p.sim.step();
		this.p4p.sim.addEvent(e); this.p4p.sim.step();
		this.p5p.sim.addEvent(e); this.p5p.sim.step();
		this.p6p.sim.addEvent(e); this.p6p.sim.step();
		this.p7p.sim.addEvent(e); this.p7p.sim.step();
		this.p8p.sim.addEvent(e); this.p8p.sim.step();

		this.t.sim.addEvent(e);   this.t.sim.step();
		this.c.sim.addEvent(e);   this.c.sim.step();
		
		printAll("After first event has been passed to all components:");
		
		// iterate
		for (int i = 0; i < 0; i++) {
			System.out.println("i = " + i);

			this.ac.sim.addEvent(e);
			this.ac.sim.step();

			this.p3p.sim.addEvent(e);
			this.p3p.sim.step();
			this.p4p.sim.addEvent(e);
			this.p4p.sim.step();
			this.p5p.sim.addEvent(e);
			this.p5p.sim.step();
			this.p6p.sim.addEvent(e);
			this.p6p.sim.step();
		}
	}

	public void printAll(String msg){
		System.out.println("------------------ BioSystem");
		System.out.println(msg);
		
		printACState(this.ac);
		printVPC(this.p3p);
		printVPC(this.p4p);
		printVPC(this.p5p);
		printVPC(this.p6p);
		printVPC(this.p7p);
		printVPC(this.p8p);
		
		printTimerVPC(this.t);

		System.out.println("------------------");
	}
	
	public static void main(String[] args) {
		System.out.println("********************");
		BioSystem bs = new BioSystem();
		bs.runMachines();		
	}
	
	// Printing methods

	/**
	 * Print current state of TopLevelAnchorCell instance
	 * @param ac
	 */
	private static void printACState(TopLevelAnchorCell ac) {
		StringBuilder builder = new StringBuilder();
		builder.append("AnchorCell={lin3=" + ac.r1.AnchorCell3.lin3);
		builder.append("; lin15=" + ac.r1.AnchorCell3.lin15);
		builder.append("; ISd0=" + ac.r1.AnchorCell3.ISd0);
		builder.append("; ISd1=" + ac.r1.AnchorCell3.ISd1);
		builder.append("; ISd2=" + ac.r1.AnchorCell3.ISd2);
		builder.append("; ISd3=" + ac.r1.AnchorCell3.ISd3);
		builder.append("}");
		System.out.println(builder.toString());
	}
	
	private static void printVPC(TopLevelVPC vpc) {
		StringBuilder builder = new StringBuilder();
		builder.append("VPC={lst=" + vpc.r1.VPC3.lst);
		builder.append("; LS=" + vpc.r1.VPC3.LS);
		builder.append("; sur2=" + vpc.r1.VPC3.sur2);
		builder.append("; let23=" + vpc.r1.VPC3.let23);
		builder.append("; sem5=" + vpc.r1.VPC3.sem5);
		builder.append("; let60=" + vpc.r1.VPC3.let60);
		builder.append("; mpk1=" + vpc.r1.VPC3.mpk1);
		builder.append("; lin12=" + vpc.r1.VPC3.lin12);
		builder.append("; dep1=" + vpc.r1.VPC3.dep1);
		builder.append("; fate=" + vpc.r1.VPC3.fate);
		builder.append("; cellcycle=" + vpc.r1.VPC3.cellcycle);
		builder.append("; counter=" + vpc.r1.VPC3.counter);
		builder.append("}");
		System.out.println(builder.toString());
	}
	
	private static void printTimerVPC(TopLevelTimer t) {
		StringBuilder builder = new StringBuilder();
		builder.append("TimerVPC={reset=" + t.r1.Timer3.reset);
		builder.append("; var1=" + t.r1.Timer3.var1);
		builder.append("; var2=" + t.r1.Timer3.var2);
		builder.append("; var3=" + t.r1.Timer3.var3);
		builder.append("; var4=" + t.r1.Timer3.var4);
		builder.append("; var5=" + t.r1.Timer3.var5);
		builder.append("; var6=" + t.r1.Timer3.var6);
		builder.append("}");
		System.out.println(builder.toString());
	}
	

	
}
